package com.apes.framework.jpa.spec;

import com.apes.framework.jpa.spec.model.Filter;
import com.apes.framework.jpa.spec.model.Group;
import com.apes.framework.plugin.esb.api.Request;
import com.apes.framework.util.MapUtil;
import com.apes.framework.util.Tools;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 功能：Jap dsl
 *
 * @author xul
 * @create 2018-07-10 15:09
 */
public class JpaDsl {
    /** SQL操作符 **/
    public final static String eq = "EQ"; /** = **/
    public final static String ne = "NE"; /** != **/
    public final static String like = "LIKE"; /** % **/
    public final static String gt = "GT"; /** > **/
    public final static String lt = "LT"; /** < **/
    public final static String gte = "GTE"; /** >= **/
    public final static String lte = "LTE"; /** <= **/
    public final static String in = "IN"; /** in **/
    public final static String notIn = "NIN"; /** not in **/

    public static <T> Specification<T> toCriteriaByEq(Object... items) {
        Assert.isTrue(items.length % 2 == 0, "参数输入数量错误！");
        int i = 0;
        Object k = null;
        Object v;
        List<Map> rows = new ArrayList();
        for (Object o : items) {
            if (i % 2 == 0) {
                k = o;
            } else {
                v = o;
                Map m = new HashMap();
                m.put("field", k);
                m.put("value", v);
                m.put("operator", "EQ");
                rows.add(m);
            }
            i++;
        }
        return toCriteria(rows);
    }

    public static <T> Specification<T> toCriteria(Object... items) {
        Assert.isTrue(items.length % 3 == 0, "参数输入数量错误！");
        int i = 0;
        Object k = null, op = null;
        Object v;
        List<Map> rows = new ArrayList();
        for (Object o : items) {
            if (i % 3 == 0) {
                k = o;
            } else if (i % 3 == 1) {
                op = o;
            } else {
                v = o;
                rows.add(MapUtil.mapper("field", k, "operator", op, "value", v));
            }
            i++;
        }
        return toCriteria(rows);
    }

    public static <T> Specification<T> toCriteriaIgnoreNull(Object... items) {
        Assert.isTrue(items.length % 3 == 0, "参数输入数量错误！");
        int i = 0;
        Object k = null, op = null;
        Object v;
        List<Map> rows = new ArrayList();
        for (Object o : items) {
            if (i % 3 == 0) {
                k = o;
            } else if (i % 3 == 1) {
                op = o;
            } else {
                v = o;
                if(o != null) rows.add(MapUtil.mapper("field", k, "operator", op, "value", v));
            }
            i++;
        }
        return toCriteria(rows);
    }

    public static List<Filter> toFilter(Object... items) {
        Assert.isTrue(items.length % 3 == 0, "参数输入数量错误！");
        int i = 0;
        Object k = null, op = null;
        Object v;
        List<Map> rows = new ArrayList();
        for (Object o : items) {
            if (i % 3 == 0) {
                k = o;
            } else if (i % 3 == 1) {
                op = o;
            } else {
                v = o;
                rows.add(MapUtil.mapper("field", k, "operator", op, "value", v));
            }
            i++;
        }
        return getFilters(rows);
    }

    public static <T> Specification<T> toCriteria(Map items) {
        List<Map> rows = getFilters(items);
        return toCriteria(rows);
    }

    public static List<Map> getFilters(Map items) {
        List<Map> rows = new ArrayList();
        items.forEach((k, v) -> {
            Map row = new HashMap();
            row.put("field", k);
            row.put("value", v);
            row.put("operator", "EQ");
            rows.add(row);
        });
        return rows;
    }

    public static <T> Specification<T> toCriteria(List items) {
        List<Filter> filters = getFilters(items);
        return build(filters);
    }

    private static List<Filter> getFilters(List<Map> o) {
        List<Filter> rows = new ArrayList();
        for (Map<String, Object> row : o) {
            rows.add(Tools.toBean(Filter.class, row)); //数据类型可以不一致
        }
        return rows;
    }

    public static <T> Specification<T> build(final List<Filter> filters) {
        Group group = new Group();
        group.setOperator("and");
        group.setFilters(filters);
        return build(group, false);
    }

    public static <T> Specification<T> build(final Group group, final boolean distinct) {
        return group.build(distinct);
    }

    public static <T> Specification<T> build(Request source, final boolean distinct) {
        Group group = getGroup(source);
        return build(group, distinct);
    }

    public static Group getGroup(Request source) {
        Group group;
        Map m = source.getMap();
        if (m.containsKey("condition")) {
            group = new Group();
            List<Map<String, Object>> rows = (List<Map<String, Object>>) m.get("condition");
            group.setFilters(rows.stream().map(m1 -> Tools.toBean(Filter.class, m1)).collect(Collectors.toList()));
        } else if (m.containsKey("group")) {
            group = source.getO("group", Group.class);
        } else {
            group = new Group();
        }

        if (m.containsKey("domain")) {
            Group domain = source.getO("domain", Group.class);
            group.addGroup(domain);
        }
        return group;
    }

    public static Group getGroup (Request request, Function<Group, Group> function) {
        return function.apply(getGroup(request));
    }


    public static Pageable getPageable (Request request) {
        Map m = request.getMap();
        int page = 0; int size = 20; // 默认值
        if (m.containsKey("page")) page = (int) m.get("page");
        if (m.containsKey("size")) size = (int) m.get("size");

        Sort sort = getSort((String) m.get("sort"));
        if (sort != null) return PageRequest.of(page, size, sort);
        return PageRequest.of(page, size);
    }

    private static Sort getSort(String str) {
        if (StringUtils.isEmpty(str)) return null;
        String[] sorts = str.split(",");
        Sorts.Builder builder = Sorts.builder();
        Arrays.stream(sorts).forEach(sort -> {
            String[] order = StringUtils.split(sort.trim(), " ");
            if (order.length == 1) {
                builder.asc(order[0].trim());
            } else {
                if (order[1].trim().equalsIgnoreCase("desc")) {
                    builder.desc(order[0].trim());
                } else {
                    builder.asc(order[0].trim());
                }
            }
        });
        if (builder.getOrders().isEmpty()) return null;
        return builder.build();
    }
}
